import java.io.IOException;
import java.util.ArrayList;

public class HexModel implements ViewListener {
	private ModelListener blackML, whiteML;
	final static int BOARDWIDTH = 10;
	final static int BOARDHEIGHT = 10;
	ColorType[][] board = new ColorType[BOARDWIDTH][BOARDHEIGHT];
	/**
	 * Constructor
	 */
	public HexModel() {
		for (int x = 0; x < BOARDWIDTH; x++) {
			for (int y = 0; y < BOARDHEIGHT; y++) {
				board[x][y] = ColorType.NONE;
			}
		}
	}

	/**
	 * TEST CONSTRUCTOR
	 * 
	 * @param c
	 *            Test number
	 */
	public HexModel(int c) {
		switch (c) {
		case 1:
			for (int x = 0; x < BOARDWIDTH; x++) {
				for (int y = 0; y < BOARDHEIGHT; y++) {
					board[x][y] = ColorType.NONE;
					if (x == y)
						board[x][y] = ColorType.WHITE;

				}
			}
			this.printBoard();
			System.out.println(isOver(ColorType.WHITE));
			board[1][1] = ColorType.BLACK;
			board[1][0] = ColorType.WHITE;
			board[2][0] = ColorType.WHITE;
			board[2][1] = ColorType.WHITE;
			this.printBoard();
			System.out.println(isOver(ColorType.WHITE));
			break;
		}
	}

	public static void main(String args[]) {
		randomPlay();
	}
	/**
	 * Checks whether a move is valid
	 * @param x X coordinate
	 * @param y Y coordinate
	 * @return boolean
	 */
	private boolean valid(int x, int y) {
		return board[x][y] == ColorType.NONE;
	}
	/**
	 * Plays randomly till a victory
	 */
	private static void randomPlay() {
		int turn = 0;
		boolean done = false;
		HexModel h = new HexModel();
		while (!done) {
			int playX, playY;
			playX = (int) (Math.random() * BOARDWIDTH);
			playY = (int) (Math.random() * BOARDHEIGHT);
			boolean valid = h.valid(playX, playY);
			while (!valid) {
				playX = (int) (Math.random() * BOARDWIDTH);
				playY = (int) (Math.random() * BOARDHEIGHT);
				valid = h.valid(playX, playY);
			}
			switch (turn % 2) {
			case 0:
				h.placePiece(playX, playY, ColorType.WHITE);

				break;
			case 1:
				h.placePiece(playX, playY, ColorType.BLACK);
				break;
			}
			turn++;
			if (h.isOver(ColorType.WHITE)) {
				done = true;
				System.out.println("WHITE WINS!");
			}
			if (h.isOver(ColorType.BLACK)) {
				done = true;
				System.out.println("BLACK WINS!");
			}
			h.printBoard();
			System.out.print("\n");
		}
	}
	/**
	 * Adds a modelListener and updates its board status
	 * @param modelListener Listener to add
	 */
	public synchronized void addModelListener(ModelListener modelListener) {

		boolean success = false;
		if (whiteML == null) {

			success = true;
			whiteML = modelListener;
			whiteML.joinSuccessful(ColorType.WHITE);
		} else {
			if (blackML == null) {
				success = true;

				blackML = modelListener;
				blackML.joinSuccessful(ColorType.BLACK);
			} else {
				modelListener.joinFailed();
			}
		}
		if (success) {
			for (int x = 0; x < BOARDWIDTH; x++) {
				for (int y = 0; y < BOARDHEIGHT; y++) {
					if (board[x][y] != ColorType.NONE)
						modelListener.placePiece(x, y, board[x][y]);
				}
			}
		}

	}

	@Override
	public synchronized void join(ViewProxy v, String session) {
		// TODO Auto-generated method stub

	}

	@Override
	public synchronized void placePiece(int x, int y, ColorType color) {
		// TODO Auto-generated method stub
		board[x][y] = color;

		if (whiteML != null)
			whiteML.placePiece(x, y, color);
		if (blackML != null)
			blackML.placePiece(x, y, color);

		// Checks for a win

		boolean won = isOver(color);
		if (won) {
			switch (color) {
			case BLACK:
				blackML.gameOver(GameOverType.WIN);
				whiteML.gameOver(GameOverType.LOSE);
				break;
			case WHITE:
				whiteML.gameOver(GameOverType.WIN);
				blackML.gameOver(GameOverType.LOSE);
				break;
			}
		}
	}
	/**
	 * Outputs board to standard out
	 */
	private void printBoard() {
		for (int y = 0; y < BOARDHEIGHT; y++) {
			System.out.print("|");
			for (int x = 0; x < BOARDWIDTH; x++) {
				switch (board[x][y]) {
				case NONE:
					System.out.print(" ");
					break;
				case WHITE:
					System.out.print("W");
					break;
				case BLACK:
					System.out.print("B");
					break;
				}
				System.out.print("|");
			}
			System.out.print("\n");
		}
	}
	/**
	 * Checks if a color has won
	 * @param c Color to check
	 * @return If the color has won
	 */
	private boolean isOver(ColorType c) {
		switch (c) {
		case BLACK: {

			int x = 0;
			boolean victory = false;

			for (int y = 0; y < BOARDHEIGHT; y++) {
				if (board[x][y] == ColorType.BLACK) {
					victory = recursiveOver(x, y, c, new ArrayList<Position>());
					if (victory)
						return victory;

				}
			}
			break;
		}
		case WHITE:
			int y = 0;
			boolean victory = false;

			for (int x = 0; x < BOARDWIDTH; x++) {
				if (board[x][y] == ColorType.WHITE) {
					victory = recursiveOver(x, y, c, new ArrayList<Position>());
					if (victory)
						return victory;

				}
			}
			break;
		}
		return false;
	}
	/**
	 * Simple x y coordinate used for win checking
	 * @author James Masco
	 *
	 */
	private class Position {
		int x, y;

		public Position(int x, int y) {
			this.x = x;
			this.y = y;
		}

		public String toString() {
			return "{" + x + "," + y + "}";
		}

		public boolean equals(Object o) {
			Position oth = (Position) o;
			return (this.x == oth.x) && (this.y == oth.y);
		}

	}
	/**
	 * Recursively checks board for a win based upon a color
	 * @param c_x Current x coordinate for the search
	 * @param c_y Current y coordinate
	 * @param c Color to search
	 * @param visited List of spots already checked
	 * @return Whether someone has won or not
	 */
	private boolean recursiveOver(int c_x, int c_y, ColorType c,
			ArrayList<Position> visited) {
		visited.add(new Position(c_x, c_y));
		if (c == ColorType.BLACK) {

			boolean victory = false;
			for (int x = -1; x <= 1; x++) {
				for (int y = -1; y <= 1; y++) {
					// All direction check
					if (x == y || (x==0 || y==0)) {
						int newX = c_x + x;
						int newY = c_y + y;
						if (newX < BOARDWIDTH && newY < BOARDHEIGHT
								&& newX >= 0 && newY >= 0) {
							Position p = new Position(newX, newY);
							if (!visited.contains(p) && board[newX][newY] == c) {
								visited.add(p);

								victory = recursiveOver(newX, newY, c, visited);
							}
							if (victory)
								break;
						} else if (newX == BOARDWIDTH) {
							return true;
						}
					}
				}
				if (victory)
					break;
			}
			return victory;

		} else {
			boolean victory = false;
			for (int x = -1; x <= 1; x++) {
				for (int y = -1; y <= 1; y++) {
					// All direction check
					if (x == y || (x==0 || y==0)) {
						int newX = c_x + x;
						int newY = c_y + y;
						if (newX < BOARDWIDTH && newY < BOARDHEIGHT
								&& newX >= 0 && newY >= 0) {
							Position p = new Position(newX, newY);
							if (!visited.contains(p) && board[newX][newY] == c) {
								visited.add(p);

								victory = recursiveOver(newX, newY, c, visited);
							}
							if (victory)
								break;
						} else if (newY == BOARDHEIGHT) {

							return true;
						}
					}
				}
				if (victory)
					break;
			}
			return victory;
		}
	}

	@Override
	public void disconnected() {
		// TODO Auto-generated method stub
		ModelListener mls[] = { whiteML, blackML };
		for (ModelListener ml:mls){
			if (ml != null)
			ml.gameOver(GameOverType.DISCONNECTED);
			
		}
		//Throw away to java garbage collection (eventually)
		whiteML = null;
		blackML = null;
		board = new ColorType[BOARDWIDTH][BOARDHEIGHT];
	}
}
